package serveur;

import client.*;
import interfaces.*;
import java.rmi.*; 
import java.util.*; 
import java.rmi.server.UnicastRemoteObject;

public class ServeurImpl extends UnicastRemoteObject implements ServeurITF {

	/*
	   Données utilisées par le serveur.
	*/
	String host;
	int    port;
	
	Vector clients = new Vector();
	Vector banned = new Vector();
	int id = 0;

	boolean server_on = true;
	boolean is_used = false;

	public ServeurImpl(String _host, int _port) throws RemoteException
	{
		host = _host;
		port = _port;
	}
	
	public String getHost() throws RemoteException
	{
		return host;
	}
	
	public int getPort() throws RemoteException
	{
		return port;
	}
	
	/*
	   Gestion de la connexion/déconnexion d'un client
	*/
	
	public boolean connect(ClientITF client) throws RemoteException {
		
		boolean time_flag;
		boolean connect_flag;
		long timer;
		
		// Si le server accepte les connexions
		connect_flag = server_on && !isBanned(client);
		if (connect_flag)
		{
			// Tant que la connexion est utilisée
			while (is_used)
			{
				// On provoque un timeout de 5s	
				timer = System.currentTimeMillis();
				time_flag = false;
			
				while (!time_flag)
				{
					time_flag = (System.currentTimeMillis() - timer > 5000);
					try { Thread.sleep(200); } catch (Exception e) {}
				}		
			}
		
			// La connexion est maintenant possible, on se "connecte"
			is_used = true;
		
			System.out.println("* Connexion du client n°" + id);
			// On attribue une capacité au client
			client.setID(id++);
		
			// Le serveur ajoute le client de sa liste
			clients.addElement(client);
		
			// On donne au client ses voisins attribués
			Vector neighbours = neighbourhood(client);
			client.setNeighbourhood(neighbours);
			
			// On prévient les voisins du nouvel arrivant		
			for (ClientITF neighbour : (Vector<ClientITF>) neighbours)
			{
				neighbour.addNeighbour(client);
			}
			
			// Affichage du graphe
			System.out.println("Graphe :\n" + printGraph());
			
			// Fin de la zone critique		
			is_used = false;
		}

		return (connect_flag);
	}

	public void disconnect(ClientITF client) throws RemoteException {

		System.out.println("* Déconnexion du client n°" + client.getID());
		
		// Le serveur retire le client de sa liste
		clients.remove(client);
		
		// On prévient les voisins du client du départ
		// pour qu'ils l'enlèvent de leur liste de voisins
		Vector neighbours = client.getNeighbourhood();
		for (ClientITF neighbour : (Vector<ClientITF>) neighbours)
		{
			neighbour.removeNeighbour(client);
			
			// Si le voisin n'a pas plus de voisins, on lui en remet
			if (neighbour.getNbNeighbours() == 0)
			{
				System.out.println("Nouveaux voisins : ");
				
				Vector new_neighbours = neighbourhood(neighbour);
				
		//		if (new_neighbours.size() != 0)
		//		{

				if(new_neighbours == null)
System.out.println("null ???");
				else
System.out.println("plop");
				
				for (ClientITF c : (Vector<ClientITF>) new_neighbours)
				{
					System.out.println("clientID : " + c.getID());
				}
				neighbour.setNeighbourhood(new_neighbours);
				
		//		}
		//		else
					System.out.println("y'a personne -_-");
			}
		}
		
		// Affichage du graphe après déconnexion
		System.out.println("Graphe :\n" + printGraph());
	}
	
	public int nbClients() throws RemoteException
	{
		return clients.size();
	}
	
	/*
	   Méthode servant pour l'affichage du graphe.
	   N'affiche rien par elle-même.
	*/
	
	public String printGraph() throws RemoteException
	{
		String r = "";
		
		int i = 0, n = clients.size();
		for (ClientITF c : (Vector<ClientITF>) clients)
		{
			r += c.getID() + " -> " + c.printNeighbours();
			
			if (i != n-1)
			{
				r += "\n";
			}
			
			i++;	
		}
		
		if (r == "")
		{
			r = "(serveur vide)";
		}
		
		return r;
	}

	/*
	   Méthode d'ajout aléatoire de voisins en fonction du nombre de clients dans le graphe.
	*/
	
	public ClientITF getByID(Vector<ClientITF> list, int id) throws RemoteException
	{
		for (ClientITF c : (Vector<ClientITF>) list)
		{
			if (c.getID() == id)
			{
				return c;
			}
		}
		
		return null;
	}
	
	public Vector neighbourhood(ClientITF client) throws RemoteException {
		
		Random generator  = new Random(System.currentTimeMillis());
		Vector neighbours = new Vector();
		
		int nbNeighbours = Math.min(clients.size()-1, 2);
		int n1, n2;
		
		// Si on doit choisir 1 ou 2 voisins
		if (nbNeighbours >= 1)
		{
			System.out.println("Entree dans le choix des voisins");
			// On choisit un premier voisin
			// On vérifie qu'on ne s'est pas choisi soi-même
			do
			{
				n1 = generator.nextInt(this.id);
				System.out.println("index: "+ n1);
			}
			while (getByID(clients,n1) == null || client.equals(getByID(clients, n1)));
			System.out.println("Fin de la boucle 1");
			// On l'ajoute à liste des voisins à donner
			neighbours.add(getByID(clients, n1));
		
			// On choisit un deuxième voisin si nécessaire
			if (nbNeighbours == 2)
			{
				// On vérifie qu'on ne s'est pas choisi soi-même
				// ou le voisin choisi avant
				do
				{
					n2 = generator.nextInt(this.id);
				}
				while (getByID(clients,n1) == null || client.equals(getByID(clients, n2)) || n1 == n2);
				
				// On l'ajoute à la liste des voisins à donner
				neighbours.add(getByID(clients, n2));
			}
		}
		
		return neighbours;
	}
	
	public boolean isBanned(ClientITF client) throws RemoteException
	{
		return banned.contains(client);
	}

	public void addToBanList(ClientITF client) throws RemoteException
	{
		if(!isBanned(client))
		{
			banned.addElement(client);
		}
	}

	public void removeFromBanList(ClientITF client) throws RemoteException
	{
		if(isBanned(client))
		{
			banned.remove(client);
		}
	}

	/*
	   Prévient la machine ayant lancé l'élection du port sur lequel elle doit écouter
	   et lui donne l'ip de la machine élue.
	*/
	public void resultatElection(int idElu, ClientITF sender) throws RemoteException
	{
		String[] r  = new String[2];
		String port = new String();
		String ip   = new String();
		
		ClientITF elu = null;
		
		//on récupère le client élu à partir de son id
		for (ClientITF c : (Vector<ClientITF>) clients)
		{
			if (idElu == c.getID())
			{
				elu = c;
			}
		}
		
		//remplissage des différents champs via la fonction du client choisiPar()
		if (elu != null)
		{
			r    = elu.choisiPar(sender);
			ip   = r[0];
			port = r[1];
			
			System.out.println("* Client ["+ sender.getID() +"] a élu ["+ elu.getID() +"]");
		
			// On prévient la personne qu'elle doit écouter pour obtenir la vidéo
			sender.videoListen(elu, ip, port);
		}
		else
		{
			System.out.println("L'élu n'a pas été trouvé !");
		}
	}

	public void switchOn() throws RemoteException
	{
		server_on = true;
	}
	
	public void switchOff() throws RemoteException
	{
		server_on = false;
	}
}
